<?php

namespace app\cms\controller\api;

use app\cms\service\UserAdminService;
use Exception;
use think\admin\Controller;
use think\exception\HttpResponseException;
use think\Response;
use WeMini\Crypt;
use WeMini\Live;
use WeMini\Qrcode;

/**
 * 微信小程序入口
 * Class Wxapp
 * @package app\cms\controller\api
 */
class Wxapp extends Controller
{
  /**
   * 接口认证类型
   * @var string
   */
  private $type = UserAdminService::API_TYPE_WXAPP;

  /**
   * 唯一绑定字段
   * @var string
   */
  private $field;

  /**
   * 小程序配置参数
   * @var array
   */
  private $cfg;

  /**
   * 接口服务初始化
   * @throws \think\db\exception\DataNotFoundException
   * @throws \think\db\exception\DbException
   * @throws \think\db\exception\ModelNotFoundException
   */
  protected function initialize()
  {
    //$opt = sysdata('wxapp');
    // $opt = [
    //   'appid' => 'wx55dd5a3c20b0b7e8',
    //   'appkey' => '8ad6d3e50f1090ac10077dbf8465575b'
    // ];
    $opt = [
      'appid' => 'wx1243a0a7acdb0cea',
      'appkey' => 'd528f4ac4e6b074db0bee2a7c1526ae4'
    ];
    $this->cfg = [
      'appid'      => $opt['appid'] ?? '',
      'appsecret'  => $opt['appkey'] ?? '',
      'cache_path' => $this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . 'wechat',
    ];
    if (empty(UserAdminService::TYPES[$this->type]['auth'])) {
      $this->error("接口类型[{$this->type}]没有定义规则");
    } else {
      $this->field = UserAdminService::TYPES[$this->type]['auth'];
    }
  }

  /**
   * 授权Code换取会话信息
   * @throws \think\admin\Exception
   * @throws \think\db\exception\DbException
   */
  public function session()
  {
    $input = $this->_vali(['code.require' => '登录凭证CODE不能为空！'], 'post');
    [$openid, $unionid, $session] = $this->applySessionKey($input['code']);
    $this->success('授权换取成功！', compact('openid', 'unionid', 'session'));
  }

  /**
   * 授权Code换取会话信息
   * @throws \think\admin\Exception
   * @throws \think\db\exception\DbException
   */
  public function auth()
  {
    $input = $this->_vali(['code.require' => '登录凭证CODE不能为空！'], 'post');
    [$openid, $unionid, $session] = $this->applySessionKey($input['code']);
    $map = UserAdminService::getUserUniMap($this->field, $openid, $unionid);
    $data = [$this->field => $openid, 'session_key' => $session];
    if (!empty($unionid)) $data['unionid'] = $unionid;
    $this->success('授权换取成功！', UserAdminService::set($map, $data, $this->type, true));
  }

  /**
   * 小程序数据解密
   */
  public function decode()
  {
    try {
      $input = $this->_vali([
        'iv.require'        => '解密向量不能为空！',
        'code.require'      => '授权CODE不能为空！',
        'encrypted.require' => '加密内容不能为空！',
      ]);
      [$openid, $unionid, $input['session_key']] = $this->applySessionKey($input['code']);
      $result = Crypt::instance($this->cfg)->decode($input['iv'], $input['session_key'], $input['encrypted']);
      if (is_array($result) && isset($result['avatarUrl']) && isset($result['nickName'])) {
        $data = [$this->field => $openid, 'nickname' => $result['nickName'], 'headimg' => $result['avatarUrl']];
        //$data['base_sex'] = ['-', '男', '女'][$result['gender']] ?? '-';
        if (!empty($unionid)) $data['unionid'] = $unionid;
        $map = UserAdminService::getUserUniMap($this->field, $openid, $unionid);
        $this->success('数据解密成功！', UserAdminService::set($map, $data, $this->type, true));
      } elseif (is_array($result)) {
        $this->success('数据解密成功！', $result);
      } else {
        $this->error('数据处理失败，请稍候再试！');
      }
    } catch (HttpResponseException $exception) {
      throw $exception;
    } catch (Exception $exception) {
      trace_file($exception);
      $this->error("数据处理失败，{$exception->getMessage()}");
    }
  }

  /**
   * 授权CODE换取会话信息
   * @param string $code 换取授权CODE
   * @return array [openid, sessionkey]
   */
  private function applySessionKey(string $code): array
  {
    try {
      $cache = $this->app->cache->get($code, []);
      if (isset($cache['openid']) && isset($cache['session_key'])) {
        return [$cache['openid'], $cache['unionid'] ?? '', $cache['session_key']];
      }
      $result = Crypt::instance($this->cfg)->session($code);
      if (isset($result['openid']) && isset($result['session_key'])) {
        $this->app->cache->set($code, $result, 60);
        return [$result['openid'], $result['unionid'] ?? '', $result['session_key']];
      } elseif (isset($result['errmsg'])) {
        $this->error($result['errmsg']);
      } else {
        $this->error("授权换取失败，请稍候再试！");
      }
    } catch (HttpResponseException $exception) {
      throw $exception;
    } catch (Exception $exception) {
      trace_file($exception);
      $this->error("授权换取失败，{$exception->getMessage()}");
    }
  }

  /**
   * 获取小程序码
   */
  public function qrcode(): Response
  {
    try {
      $data = $this->_vali([
        'size.default' => 430,
        'type.default' => 'base64',
        'path.require' => '跳转路径不能为空!',
      ]);
      $result = Qrcode::instance($this->cfg)->createMiniPath($data['path'], $data['size']);
      if ($data['type'] === 'base64') {
        $this->success('生成小程序码成功！', [
          'base64' => 'data:image/png;base64,' . base64_encode($result),
        ]);
      } else {
        return response($result)->contentType('image/png');
      }
    } catch (HttpResponseException $exception) {
      throw $exception;
    } catch (Exception $exception) {
      trace_file($exception);
      $this->error($exception->getMessage());
    }
  }

  /**
   * 获取直播列表
   */
  public function getLiveList()
  {
    try {
      $data = $this->_vali(['start.default' => 0, 'limit.default' => 10]);
      $list = Live::instance($this->cfg)->getLiveList($data['start'], $data['limit']);
      $this->success('获取直播列表成功！', $list);
    } catch (HttpResponseException $exception) {
      throw $exception;
    } catch (Exception $exception) {
      trace_file($exception);
      $this->error($exception->getMessage());
    }
  }

  /**
   * 获取回放源视频
   */
  public function getLiveInfo()
  {
    try {
      $data = $this->_vali([
        'start.default'   => 0,
        'limit.default'   => 10,
        'action.default'  => 'get_replay',
        'room_id.require' => '直播间不能为空',
      ]);
      $result = Live::instance($this->cfg)->getLiveInfo($data);
      $this->success('获取回放视频成功！', $result);
    } catch (HttpResponseException $exception) {
      throw $exception;
    } catch (Exception $exception) {
      trace_file($exception);
      $this->error($exception->getMessage());
    }
  }
}
